package com.nvwa.localcache;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

/**
 * 本地缓存工具
 */
public class LocalCacheHelper extends ConcurrentHashMap<String, CacheData<Object>> {

    /**
     * 单例
     */
    public final static LocalCacheHelper CACHE = new LocalCacheHelper();

    private LocalCacheHelper() {
    }

    static {
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                for (String key : CACHE.keySet()) {
                    Object obj = CACHE.scheduleGet(key);
                    if (obj == null) {
                        CACHE.remove(key);
                    }
                }
            }
        }, 0, 10 * 1000); // 10 秒钟清理一次
    }

    /**
     * 定期删除任务获取缓存池中对象
     *
     * @param key 缓存 KEY
     * @return 缓存未过期对象
     */
    private Object scheduleGet(String key) {
        CacheData<Object> data = super.get(key);
        long now = new Date().getTime();
        //永久、未过期、有加载器，不删除
        if (data.expire <= 0 || now <= data.deadTime || data.load != null) {
            return data.data;
        }
        return null;
    }

    /**
     * 获取缓存中的数据
     *
     * @param key 缓存 KEY
     * @return 缓存的数据，找不到返回或者过期则直接返回 null
     */
    public Object get(String key) {
        CacheData<Object> data = super.get(key);
        if (data == null) {
            return null;
        }

        long now = new Date().getTime();
        //永久数据或者未过期数据
        if (data.expire <= 0 || now <= data.deadTime) {
            return data.data;
        }
        //有加载器的数据
        if (data.load != null) {
            Object value = data.load.get();
            data.data = value;
            data.deadTime = now + data.expire;// 重新算过存活时间
            return value;
        }
        //已过期数据并且没有加载器(清理器未及时清理的数据)
        super.remove(key);
        return null;
    }

    /**
     * 获取缓存中的数据（避免强类型转换的麻烦）
     *
     * @param <T> 缓存的类型
     * @param key 缓存 KEY
     * @param clz 缓存的类型
     * @return 缓存的数据，找不到返回或者过期则直接返回 null
     */
    @SuppressWarnings({"unchecked"})
    public <T> T get(String key, Class<T> clz) {
        Object obj = get(key);
        return obj == null ? null : (T) obj;
    }

    /**
     * @param key    缓存 KEY
     * @param data   要缓存的数据
     * @param expire 过期时间
     */
    public void put(String key, Object data, int expire) {
        put(key, new CacheData<>(data, expire));
    }

    /**
     * @param key    缓存 KEY
     * @param data   要缓存的数据
     * @param expire 过期时间
     * @param load   数据装载器，如果缓存中没有数据或者已经过期，则调用数据装载器加载最新的数据并且加入缓存，并返回（相当与永久缓存）
     */
    public void put(String key, Object data, int expire, Supplier<Object> load) {
        put(key, new CacheData<>(data, expire, load));
    }

}
